﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Matriculex.UI.Helpers;
using log4net;
using Matriculex.Dominio;

namespace Matriculex.UI.Controllers
{
    public partial class RematriculaController
    {
        private ActionResult AlunoAberto(AlunoAberto aluno)
        {
            if (aluno.Inadimplente)
                return View("Inadimplente");
            if (aluno.Bolsista)
                return View("Bolsista");
            PeriodoRematricula periodo = PeriodoRematricula.GetPeriodoVigente();
            if (periodo == null)
                return View("NaoExiste");

            return RetornaViewAlunosAbertos(aluno);
        }

        private ActionResult RetornaViewAlunosAbertos(AlunoAberto aluno)
        {
            ViewData["oferecimentosCursáveis"] = aluno.GetOferecimentosCursáveis();
            ViewData["matriculados"] = aluno.GetOferecimentosMatriculadosPeriodoVigente();
            ViewData["trocas_trocaveis"] = new SelectList(GetDisciplinasTrocáveis(aluno), "Id", "NomeCompleto");
            ViewData["trocas_destinos"] = new SelectList(GetDisciplinasDestinoTroca(aluno), "Id", "NomeCompleto");

            return View("AlunoAberto");
        }

        private IEnumerable<Disciplina> GetDisciplinasTrocáveis(AlunoAberto aluno)
        {
            return aluno.GetDisciplinasNaoCursadas();
        }

        private IEnumerable<Disciplina> GetDisciplinasDestinoTroca(AlunoAberto aluno)
        {
            var disciplinasNaoSelecionadas = Disciplina.FindAll().Where(d => !aluno.DisciplinasPreSelecionadas.Contains(d));
            var disciplinasDestino = from ofer in Oferecimento.FindAllByPeriodoVigente()
                                     where disciplinasNaoSelecionadas.Contains(ofer.Disciplina)
                                     && ofer.VagasRestantes > 0
                                     select ofer.Disciplina;
            return disciplinasDestino;
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult AlunoAberto(string[] ofsSelecionados, FormCollection form)
        {
            AlunoAberto aluno = ControllerContext.CurrentUser<AlunoAberto>();
            if(aluno == null)
                return RedirectToAction("Index", "Home");

            try
            {
                aluno.CancelaMatriculas(); // primeiro cancela todas as matrículas deste aluno
                RealizaTrocas(form, aluno);
                RealizaRematriculas(ofsSelecionados, aluno);
                aluno.Save();
                aluno.Refresh();

                ViewData["message"] = "Rematrícula realizada com sucesso.";
            }
            catch (DadosInvalidosException e)
            {
                ViewData["error"] = string.IsNullOrEmpty(e.Message) ?
                    "Houve um problema ao processar sua rematrícula. Por favor, tente novamente."
                    : e.Message;
            }
            catch (EsgotadoException e)
            {
                ViewData["error"] = e.Message;
            }
            catch (Exception e)
            {
                ViewData["error"] = "Houve um problema ao salvar a sua rematrícula. Por favor, tente novamente.";
                logger.Error("Erro ao salvar a rematrícula de um aluno", e);
            }
            return RetornaViewAlunosAbertos(aluno);
        }

        /// <summary>
        /// Descobre se o aluno quer fazer alguma troca de disciplina, e realiza-as.
        /// </summary>
        /// <param name="form"></param>
        /// <param name="aluno"></param>
        /// <exception cref="DadosInvalidosException"></exception>
        private void RealizaTrocas(FormCollection form, AlunoAberto aluno)
        {
            // convenção: grade denota uma disciplina que VAI ser trocada
            int idGrade1 = -1, idGrade2 = -1, idDestino1 = -1, idDestino2 = -1;
            bool troca1 = false; // devemos realizar idGrade1 -> idDestino1?
            bool troca2 = false; // devemos realizar idGrade2 -> idDestino2?
            #region validação simples e parse dos dados
            string sIdGrade1 = form["troca-grade-1"];
            string sIdGrade2 = form["troca-grade-2"];
            string sIdDestino1 = form["troca-1"];
            string sIdDestino2 = form["troca-2"];

            if (string.IsNullOrEmpty(sIdGrade1) && string.IsNullOrEmpty(sIdGrade2))
                return; // nada a fazer
            troca1 = !string.IsNullOrEmpty(sIdGrade1) && !string.IsNullOrEmpty(sIdDestino1);
            troca2 = !string.IsNullOrEmpty(sIdGrade2) && !string.IsNullOrEmpty(sIdDestino2);
            if (!troca1 && !troca2)
                throw new DadosInvalidosException("Você deve selecionar ao menos uma disciplina para troca."); 
            if (troca1)
                if (!(int.TryParse(sIdGrade1, out idGrade1) &&
                int.TryParse(sIdDestino1, out idDestino1)))
                    throw new DadosInvalidosException();
            if (troca2)
                if (!(int.TryParse(sIdGrade2, out idGrade2) &&
                    int.TryParse(sIdDestino2, out idDestino2)))
                    throw new DadosInvalidosException();
            #endregion
            Oferecimento[] ofsVigentes = Oferecimento.FindAllByPeriodoVigente();
            if (troca1)
            {
                Oferecimento destino1 = ofsVigentes.Where(of => of.Disciplina.Id == idDestino1).FirstOrDefault();
                if (destino1 == null)
                    throw new DadosInvalidosException();
                RealizaTroca(idGrade1, destino1, aluno);
            }
            if (troca2)
            {
                Oferecimento destino2 = Oferecimento.FindAllByPeriodoVigente().Where(of => of.Disciplina.Id == idDestino2).FirstOrDefault();
                if (destino2 == null)
                    throw new DadosInvalidosException();
                RealizaTroca(idGrade2, destino2, aluno);
            }
        }

        /// <summary>
        /// Realiza uma troca de disciplina do aluno.
        /// </summary>
        /// <param name="idGrade">O id da disciplina a ser trocada</param>
        /// <param name="idDestino">O id da nova disciplina</param>
        /// <exception cref="DadosInvalidosException">Se o id passado for inválido</exception>
        private void RealizaTroca(int idGrade, Oferecimento ofDestino, AlunoAberto aluno)
        {
            Disciplina grade = Disciplina.TryFind(idGrade);
            Disciplina destino = ofDestino.Disciplina;
            if (grade == null || destino == null)
                throw new DadosInvalidosException();
            try
            {
                aluno.TrocarDisciplina(grade, destino);
            }
            catch (ArgumentException e)
            {
                throw new DadosInvalidosException("",e);
            }
            try
            {
                aluno.Matricula(ofDestino);
            }
            catch (Exception)
            {
                // desfaz troca
                aluno.TrocarDisciplina(destino, grade);
                throw;
            }
        }

        /// <summary>
        /// Matricula o aluno nos oferecimentos de id passados.
        /// </summary>
        /// <exception cref="DadosInvalidosException"></exception>
        /// <exception cref="EsgotadoException"></exception>
        private void RealizaRematriculas(string[] ofsSelecionados, AlunoAberto aluno)
        {
            if (ofsSelecionados == null || ofsSelecionados.Length == 0)
                throw new DadosInvalidosException("Você deve selecionar ao menos uma disciplina.");

            List<Oferecimento> oferecimentos = new List<Oferecimento>();
            foreach (string sId in ofsSelecionados)
            {
                int id;
                if (!int.TryParse(sId, out id))
                    throw new DadosInvalidosException();
                Oferecimento of = Oferecimento.TryFind(id);
                if (of == null)
                    throw new DadosInvalidosException();
                aluno.Matricula(of);
            }
        }
    }
}
